﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using NCommet.Core;
using NCommet.Testing;
using System.Reflection;
using NCommet.Core.Configuration;
using NCommet.Core.Services;
using NCommet.Core.Agents;
using System.Security.Principal;
using System.Web.Security;
using NCommet.Testing.SampleContent;

namespace NCommet.TestGUI
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        protected string ItemTypeFilter
        {
            get { return txtItemTypeFilter.Text; }
            set { txtItemTypeFilter.Text = value; }
        }

        private static TreeNode AddNode(TreeNodeCollection tnc, Item it)
        {
            if (tnc == null)
                throw new ArgumentException("Node collection must be not null", "tnc");
            TreeNode t = new TreeNode();
            UpdateNode(t, it);
            tnc.Add(t);
            return t;
        }

        private static void UpdateNode(TreeNode t)
        {
            UpdateNode(t, ItemFromNode(t));
        }

        private static void UpdateNode(TreeNode t, Item it)
        {
            if (t == null)
                throw new ArgumentException("Node must be not null", "t");
            if (it == null)
                t.Text = "ERRONEOUS NODE";
            else
                t.Text = string.Format("{0}: {1} ({2})", it.Id, it.Name, it.ItemType);
            t.Tag = it;
        }

        private static void UpdateNodes(TreeNodeCollection t)
        {
            foreach (TreeNode tn in t)
            {
                UpdateNode(tn);
                UpdateNodes(tn.Nodes);
            }
        }

        private static TreeNode FindNode(TreeNodeCollection tnc, Item item)
        {
            foreach (TreeNode tn in tnc)
            {
                if (item == (Item)tn.Tag)
                    return tn;
            }
            return null;
        }

        private TreeNode FindNode(Item item)
        {
            IList<Item> path = HierarchyManager.GetAncestorPath(item, true);

            TreeNodeCollection tnc = treeView1.Nodes;

            TreeNode tn = null;
            for (int i = 0; i < path.Count; i++)
            {
                tn = FindNode(tnc, path[i]);
                if (tn == null)
                    return null;
                tnc = tn.Nodes;
            }
            return tn;
        }

        private void FillNodes(TreeNodeCollection tnc, IEnumerable<Item> items)
        {
            foreach (Item it in items)
            {
                TreeNode t = AddNode(tnc, it);
                if (it != null) // SHOULDN't HAPPEN but it did due to bug?
                {
                    if (string.IsNullOrEmpty(ItemTypeFilter))
                        FillNodes(t.Nodes, it.Children);
                    else
                        FillNodes(t.Nodes, it.FilterChildren(ItemTypeFilter));
                }
            }
        }

        private static Item ItemFromNode(TreeNode tn) { return (Item)tn.Tag; }
        private Item ReloadItemFromNode(TreeNode tn) { return Item.Get(ItemFromNode(tn).Id); }

        private void Form1_Load(object sender, EventArgs e)
        {
            Fill_cmbCurrentUser();

            EventSink.Instance.ItemAdded += new ItemHandler(Instance_ItemAdded);
            EventSink.Instance.ItemSaved += new ItemHandler(Instance_ItemSaved);
            EventSink.Instance.ItemChangedParent += new ItemChangedParentHandler(Instance_ItemChangedParent);
            EventSink.Instance.ItemDeleting += new ItemCancellableHandler(Instance_ItemDeleting);
            EventSink.Instance.ItemDeleted += new ItemHandler(Instance_ItemDeleted);
            EventSink.Instance.ChildrenCleared += new ItemHandler(Instance_ChildrenCleared);
            EventSink.Instance.ChildrenReordered += new ItemHandler(Instance_ChildrenReordered);
            RebuildTree();

            cmbContentType.Items.Add(typeof(SimpleContent).AssemblyQualifiedName);
            cmbContentType.Items.Add(typeof(SimpleContent2).AssemblyQualifiedName);
            cmbContentType.Items.Add(typeof(MyContent).AssemblyQualifiedName);
        }

        private bool treeViewLocked = false;
        private bool TreeViewLock()
        {
            lock (this)
            {
                if (treeViewLocked)
                    return false;
                treeViewLocked = true;
                return true;
            }
        }

        private void TreeViewUnlock()
        {
            lock (this)
            {
                treeViewLocked = false;
            }
        }

        void Instance_ChildrenReordered(object sender, ItemEventArgs e)
        {
            if (!TreeViewLock()) return;
            try
            {
                TreeNode tn = FindNode(e.Item);
                RebuildNode(tn);
            }
            finally
            {
                TreeViewUnlock();
            }
        }

        void Instance_ChildrenCleared(object sender, ItemEventArgs e)
        {
            if (!TreeViewLock()) return;
            try
            {
                TreeNode tn = FindNode(e.Item);
                tn.Nodes.Clear();
            }
            finally
            {
                TreeViewUnlock();
            }
        }

        void Instance_ItemDeleted(object sender, ItemEventArgs e)
        {
            if (!TreeViewLock()) return;
            try
            {
                TreeNode tn = FindNode(e.Item);
                if (tn != null)
                    tn.Remove();

            }
            finally
            {
                TreeViewUnlock();
            }
        }

        void Instance_ItemDeleting(object sender, ItemCancellableEventArgs e)
        {
            e.Cancel = DialogResult.Yes !=
                MessageBox.Show(this, "Really delete node " + e.Item.Id + "?", "Confirm", MessageBoxButtons.YesNo);
        }

        private TreeNodeCollection NodeCollectionOf(Item item)
        {
            if (item == null)
                return treeView1.Nodes;
            else
                return FindNode(item).Nodes;
        }

        void Instance_ItemChangedParent(object sender, ItemChangedParentArgs e)
        {
            if (!TreeViewLock()) return;
            try
            {
                TreeNodeCollection oldTNC = NodeCollectionOf(e.OldParent);
                TreeNodeCollection newTNC = NodeCollectionOf(e.Item.Parent);
                TreeNode oldNode = FindNode(oldTNC, e.Item);
                if (oldNode == null)
                    oldNode = AddNode(newTNC, e.Item);
                else
                {
                    oldTNC.Remove(oldNode);
                    newTNC.Add(oldNode);
                }
            }
            finally
            {
                TreeViewUnlock();
            }
        }

        void Instance_ItemSaved(object sender, ItemEventArgs e)
        {
            if (!TreeViewLock()) return;
            try
            {
                TreeNodeCollection tnc = NodeCollectionOf(e.Item.Parent);
                TreeNode tn = FindNode(tnc, e.Item);
                if (tn == null)
                    tn = AddNode(tnc, e.Item);
                else
                    UpdateNode(tn, e.Item);
                UpdateNodes(tn.Nodes); // catch updates on item children
            }
            finally
            {
                TreeViewUnlock();
            }
        }

        void Instance_ItemAdded(object sender, ItemEventArgs e)
        {
            if (!TreeViewLock()) return;
            try
            {
                Instance_ItemSaved(sender, e);
            }
            finally
            {
                TreeViewUnlock();
            }
        }

        private void RebuildTree()
        {
            ICollection<Item> lstRoots = string.IsNullOrEmpty(ItemTypeFilter) ? HierarchyManager.GetRoots() : HierarchyManager.GetRoots(ItemTypeFilter);
            treeView1.Nodes.Clear();
            FillNodes(treeView1.Nodes, lstRoots);
        }

        private void RebuildNode(TreeNode treeNode)
        {
            Item item = ReloadItemFromNode(treeNode);
            treeNode.Nodes.Clear();
            FillNodes(treeNode.Nodes, item.Children);
        }

        private void ReSortNode(TreeNode treeNode, int Key)
        {
            Item item = ReloadItemFromNode(treeNode);
            treeNode.Nodes.Clear();
            NCommet.Core.Agents.ISorter dbi = NCommetContainer.Instance.Sorter;

            FillNodes(treeNode.Nodes, dbi.Sort(Item.ToIntList(item.Children), Key));
        }

        private void btnAddRootNode_Click(object sender, EventArgs e)
        {
            Item.Create("new item", "Item Type", true);
        }

        private int SelectedTreeNodeItemID
        {
            get
            {
                if (treeView1 != null && treeView1.SelectedNode != null)
                    return ItemFromNode(treeView1.SelectedNode).Id;
                else
                    return 0;
            }
        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            RefreshSelectedItem();
        }

        private void RefreshSelectedItem()
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            // Tab 1 : Attributes
            txtName.Text = item.Name;
            txtItemType.Text = item.ItemType;

            // Tab 2 : Relations
            lvRelations.Items.Clear();
            foreach (RelItem relItem in item.Relatives)
            {
                ListViewItem lvi;
                lvRelations.Items.Add(lvi = new ListViewItem(new string[]{
                    Convert.ToString(relItem.Relative.Id),
                    relItem.Relative.Name, relItem.RelationType}));
                lvi.Tag = relItem;
            }

            // Tab 3 : Reverse Relations
            lvReverseRelations.Items.Clear();
            foreach (RelItem relItem in item.GetReverseRelItems())
            {
                ListViewItem lvi;
                lvReverseRelations.Items.Add(lvi = new ListViewItem(new string[]{
                    Convert.ToString(relItem.Owner.Id),
                    relItem.Owner.Name, relItem.RelationType}));
                lvi.Tag = relItem;
            }

            // Tab 4 : Content
            panContent.Controls.Clear();
            if (item.Content == null)
            {
                Label lbl;
                lbl = new Label();
                lbl.Text = "This item doesn't have content.";
                panContent.Controls.Add(lbl);
            }
            else
            {
                PropertyInfo[] pis = item.Content.GetType().GetProperties();
                int y = 8;
                foreach (PropertyInfo pi in pis)
                {
                    if (pi.PropertyType == typeof(string))
                    {
                        Label lbl = new Label();
                        lbl.Text = pi.Name;
                        lbl.Location = new Point(8, y);
                        lbl.Size = new Size(100, 20);
                        panContent.Controls.Add(lbl);

                        if (pi.IsDefined(typeof(RichTextAttribute), false))
                        {
                            RichTextBox rtb = new RichTextBox();
                            rtb.Location = new Point(120, y);
                            rtb.Size = new Size(panContent.ClientSize.Width - rtb.Location.X - 8, 300);
                            rtb.Rtf = Convert.ToString(pi.GetValue(item.Content, null));
                            rtb.Tag = pi.Name; // remember property name
                            rtb.Anchor = AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top;
                            panContent.Controls.Add(rtb);
                            y += 302;
                        }
                        else
                        {
                            TextBox txt = new TextBox();
                            txt.Location = new Point(120, y);
                            txt.Size = new Size(panContent.ClientSize.Width - txt.Location.X - 8, 20);
                            txt.Text = Convert.ToString(pi.GetValue(item.Content, null));
                            txt.Tag = pi.Name; // remember property name
                            txt.Anchor = AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top;
                            panContent.Controls.Add(txt);
                            y += 22;
                        }
                    }
                }
            }
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            item.Name = txtName.Text;
            //item.ItemType = txtItemType.Text;
            item.Save();
        }

        private void btnAddRelation_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            Item relative = Item.Get(Convert.ToInt32(txtRelative.Text));
            item.Relatives.Add(relative, txtRelationType.Text);
            item.Save();
        }

        private void mnuDeleteRelation_Click(object sender, EventArgs e)
        {
            ListView lv;
            if (tabControl1.SelectedTab == tabRelations)
                lv = lvRelations;
            else if (tabControl1.SelectedTab == tabReverseRelations)
                lv = lvReverseRelations;
            else
                lv = null;
            foreach (ListViewItem lvi in lv.SelectedItems)
            {
                RelItem relItem = lvi.Tag as RelItem;
                if (relItem != null)
                {
                    relItem.Delete();
                    relItem.Owner.Save();
                }
            }

            RefreshSelectedItem();
        }

        #region Content Tab
        private void btnDeleteContent_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            item.Content = null;
            item.Save();
            RefreshSelectedItem();
        }

        private void btnAddContent_Click(object sender, EventArgs e)
        {
            string typeName = Convert.ToString(cmbContentType.SelectedItem);
            Type type = Type.GetType(typeName);
            ContentBase content = type.GetConstructor(Type.EmptyTypes).Invoke(null) as ContentBase;
            Item item = Item.Get(SelectedTreeNodeItemID);
            item.Content = content;
            item.Save();
            RefreshSelectedItem();
        }

        private void btnRevert_Click(object sender, EventArgs e)
        {
            RefreshSelectedItem();
        }

        private void btnSaveContent_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            if (item.Content == null)
                return;

            foreach (Control ctl in panContent.Controls)
            {
                if (ctl.Tag == null || !(ctl.Tag is string))
                    continue;
                string PropertyName = (string)ctl.Tag;
                PropertyInfo pi = item.Content.GetType().GetProperty(PropertyName);
                if (pi == null)
                    continue;

                if (ctl is RichTextBox)
                {
                    RichTextBox rtb = (RichTextBox)ctl;
                    pi.SetValue(item.Content, rtb.Rtf, null);
                }
                else if (ctl is TextBox)
                {
                    TextBox txt = (TextBox)ctl;
                    pi.SetValue(item.Content, txt.Text, null);
                }
            }
            item.Save();
            RefreshSelectedItem();
        }

        #endregion

        #region TreeView Drag & Drop

        private void treeView1_DragDrop(object sender, DragEventArgs e)
        {
            TreeNode nodeToMove = (TreeNode)e.Data.GetData(typeof(TreeNode));
            Point pt = new Point(e.X, e.Y);
            pt = treeView1.PointToClient(pt);
            TreeNode nodeTarget = treeView1.GetNodeAt(pt);

            Item itemToMove = ReloadItemFromNode(nodeToMove);
            Item itemTarget = nodeTarget != null ? ReloadItemFromNode(nodeTarget) : null;
            itemToMove.ChangeParent(itemTarget);
        }

        private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
        {
            Console.WriteLine(e.ToString());

            DoDragDrop(e.Item, DragDropEffects.All);
        }

        private void treeView1_DragOver(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Move;
        }

        #endregion

        private void btnRefreshTree_Click(object sender, EventArgs e)
        {
            RebuildTree();
        }

        #region Tree View Context Menu Handlers

        private void mnuDetachNode_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            item.Detach();
            //RebuildTree();
        }

        private void mnuMoveDown_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            item.MoveDown();
        }

        private void mnuMoveUp_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            item.MoveUp();
        }

        private void mnuDeleteNode_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            item.Delete();
        }

        private void mnuAddChildNode_Click(object sender, EventArgs e)
        {
            Item parent = Item.Get(SelectedTreeNodeItemID);
            parent.Children.Add(Item.Create("child", "Item Type"));
            parent.Save();
        }

        private void mnuNodeClear_Click(object sender, EventArgs e)
        {
            Item parent = Item.Get(SelectedTreeNodeItemID);
            parent.Children.Clear();
            parent.Save();
        }

        #endregion

        private void btnFilterItemType_Click(object sender, EventArgs e)
        {
            RebuildTree();
        }

        private void btnRemoveItemTypeFilter_Click(object sender, EventArgs e)
        {
            ItemTypeFilter = string.Empty;
            RebuildTree();
        }

        private void cmsTreeView_Opening(object sender, CancelEventArgs e)
        {
            mnuSortChildren.DropDownItems.Clear();

            // Get them dynamically instead

            ToolStripItem tsi = mnuSortChildren.DropDownItems.Add("By Title");
            tsi.Tag = 1;
            tsi.Click += new EventHandler(mnuSortNode_Click);

            tsi = mnuSortChildren.DropDownItems.Add("By MyOtherString");
            tsi.Tag = 2;
            tsi.Click += new EventHandler(mnuSortNode_Click);

            mnuSortChildren.Enabled = mnuSortChildren.DropDownItems.Count > 0;
        }

        public void mnuSortNode_Click(object sender, EventArgs e)
        {
            ToolStripItem s = sender as ToolStripItem;
            if (s != null)
                ReSortNode(treeView1.SelectedNode, (int)s.Tag);
        }

        private void treeView1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                treeView1.SelectedNode = treeView1.GetNodeAt(e.X, e.Y);
            }
        }

        private void mnuShallowCopy_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            Item n = item.ShallowClone(true);
            n.Save();

        }

        private void mnuDeepCopy_Click(object sender, EventArgs e)
        {
            Item item = Item.Get(SelectedTreeNodeItemID);
            Item n = item.DeepClone(true);
            n.Save();
        }

        private void mnuNodeProperties_Click(object sender, EventArgs e)
        {
            NodePropertiesForm frm = new NodePropertiesForm();
            frm.LoadItem(Item.Get(SelectedTreeNodeItemID));
            frm.Show();
        }

        private void cmbCurrentUser_SelectedIndexChanged(object sender, EventArgs e)
        {
            TestWhoAmI t = (TestWhoAmI)NCommetContainer.Instance.WhoAmI;
            t.CurrentUser = cmbCurrentUser.SelectedItem.ToString();
        }

        private void Fill_cmbCurrentUser()
        {
            foreach (MembershipUser mu in Membership.GetAllUsers())
                cmbCurrentUser.Items.Add(mu.UserName);
        }


    }

    /// <summary>
    /// Tagging class for the GUI
    /// </summary>
    class RichTextAttribute : Attribute
    {
    }

    [Serializable]
    public class MyContent : ContentBase
    {
        private string title;
        public virtual string Title
        {
            get { return title; }
            set { title = value; }
        }

        private string summary;

        [RichText]
        public virtual string Summary
        {
            get { return summary; }
            set { summary = value; }
        }
    }

    #region IWhoAmI Implementation

    public class TestWhoAmI : IWhoAmI
    {
        private string currentUser = null;
        private IPrincipal currentPrincipal = null;

        public string CurrentUser
        {
            get { return currentUser; }

            internal set
            {
                currentUser = value;
                currentPrincipal = new GenericPrincipal(new GenericIdentity(currentUser), GetRolesOfUser(currentUser));
            }
        }

        #region IWhoAmI Members

        public IPrincipal GetCurrentUser()
        {
            return currentPrincipal;
        }

        public string[] GetRolesOfUser(string username)
        {
            return Roles.GetRolesForUser(username);
        }

        public string[] GetAvailableRoles()
        {
            return Roles.GetAllRoles();
        }

        #endregion

    }


    //public class TestWhoAmI : IWhoAmI, IModule
    //{
    //    public string[] ALL_USERS = new string[] {
    //        "user1", "user2"};
    //    private string[] ALL_ROLES = new string[] { "editor", "editor of lesson", "admin" };
    //    private string[][] MAP = new string[][] {
    //        new string[] { "editor", "admin" },
    //        new string[] {"editor of lesson", "admin"}};

    //    private string currentUser = null;
    //    private IPrincipal currentPrincipal = null;

    //    public string CurrentUser
    //    {
    //        get { return currentUser; }

    //        internal set
    //        {
    //            currentUser = value;
    //            currentPrincipal = new GenericPrincipal(new GenericIdentity(currentUser), GetRolesOfUser(currentUser));
    //        }
    //    }

    //    #region IWhoAmI Members

    //    public System.Security.Principal.IPrincipal GetCurrentUser()
    //    {
    //        return currentPrincipal;
    //    }

    //    public string[] GetRolesOfUser(string username)
    //    {
    //        for (int i = 0; i < ALL_USERS.Length; i++)
    //            if (ALL_USERS[i] == username)
    //                return MAP[i];
    //        return new string[0];
    //    }

    //    public string[] GetAvailableRoles()
    //    {
    //        return ALL_ROLES;
    //    }

    //    #endregion

    //    #region IModule Members

    //    public void InitModule()
    //    {
    //        Container.Instance.AuthorizationManager.WhoAmI = this;
    //    }

    //    #endregion
    //}
    #endregion
}